package com.eastandroid.zoom;

import java.util.Observable;

import com.epic.pager.OnModePagerListener;

import android.graphics.Matrix;
import android.graphics.Matrix.ScaleToFit;
import android.graphics.RectF;
import android.util.FloatMath;
import android.util.Log;

public class ZoomState extends Observable {
	private static final String TAG = "ZoomState";
	private OnModePagerListener listener;

	private Matrix mZoomMatrix = new Matrix();
	private Matrix mAdjSizeMatrix = new Matrix();
	private Matrix mPositionMatrix = new Matrix();

	private Matrix mCalcTempMatrix = new Matrix();

	private float[] mSrc = new float[4];
	private float[] mDst = new float[4];

	private RectF mSrcRect = new RectF();
	private RectF mDstRect = new RectF();
	private RectF mOriginalRect = new RectF();
	private RectF mViewRect = new RectF();

	public enum ZoomStyle {
		POLY, RECT
	};

	private ZoomStyle flagZoomType = ZoomStyle.RECT;

	public enum MinSize {
		NONE, PAGE, WIDTH
	};

	private MinSize flagMinSize = MinSize.NONE;

	public enum Position {
		NONE, CENTERIN, INSIDE
	};

	private Position flagPosition = Position.NONE;

	public enum UserTouch {
		NONE, TOUCHIGNORE, TOUCHAFTER
	};

	private UserTouch flagUserTouch = UserTouch.TOUCHIGNORE;

	private float scaleDefault;

	private float defaultX;

	private float defaultY;

	public synchronized Matrix getMatrix() {
		return mZoomMatrix;
	}

	public void setOriginalRect(float l, float t, float r, float b) {
		mOriginalRect.set(l, t, r, b);
	}

	public void setFrameRect(float l, float t, float r, float b) {
		mViewRect.set(l, t, r, b);
	}

	public void reset() {
		mZoomMatrix.reset();
		if (mViewRect.width() > mViewRect.height()) {
			scaleDefault = mViewRect.width() / mOriginalRect.width();
		} else {
			scaleDefault = mViewRect.height() / mOriginalRect.height();
		}
		mZoomMatrix.postScale(scaleDefault, scaleDefault,
				mOriginalRect.centerX(), mOriginalRect.centerY());
		defaultX = (mViewRect.width() - mOriginalRect.width()) / 2f;
		defaultY = (mViewRect.height() - mOriginalRect.height()) / 2f;
		mZoomMatrix.postTranslate(defaultX, defaultY);
		setChanged();
	}

	public void setMinSize(MinSize minsize) {
		flagMinSize = minsize;
		setChanged();
	}

	public void setZoomStyle(ZoomStyle zoomStyle) {
		flagZoomType = zoomStyle;
		setChanged();
	}

	public void setPosition(Position position) {
		flagPosition = position;
		setChanged();
	}

	public void setUserTouch(UserTouch userTouch) {
		flagUserTouch = userTouch;
		setChanged();
	}

	public void initPinch(float x1, float y1, float x2, float y2) {
		if (flagZoomType == ZoomStyle.POLY) {
			mSrc[0] = x1;
			mSrc[1] = y1;
			mSrc[2] = x2;
			mSrc[3] = y2;
			mDst[0] = x1;
			mDst[1] = y1;
			mDst[2] = x2;
			mDst[3] = y2;
		} else if (flagZoomType == ZoomStyle.RECT) {
			outRect_of_outCircle(mSrcRect, x1, y1, x2, y2);
			mDstRect.set(mSrcRect);
		}
	}

	public void zoom(float x1, float y1, float x2, float y2) {
		calcZoomMatrix(x1, y1, x2, y2);
		if (flagUserTouch == UserTouch.TOUCHIGNORE)
			calcAdjuctMatrix();
		setChanged();
	}

	public synchronized void exitPinch(float x1, float y1, float x2, float y2) {
		calcZoomMatrix(x1, y1, x2, y2);
		if (flagUserTouch == UserTouch.TOUCHAFTER)
			calcAdjuctMatrix();
		setChanged();
	}

	private void calcZoomMatrix(float x1, float y1, float x2, float y2) {
		if (flagZoomType == ZoomStyle.POLY) {
			mDst[0] = x1;
			mDst[1] = y1;
			mDst[2] = x2;
			mDst[3] = y2;
			mCalcTempMatrix.setPolyToPoly(mSrc, 0, mDst, 0, mSrc.length >> 1);
			mSrc[0] = x1;
			mSrc[1] = y1;
			mSrc[2] = x2;
			mSrc[3] = y2;
		} else if (flagZoomType == ZoomStyle.RECT) {
			outRect_of_outCircle(mDstRect, x1, y1, x2, y2);
			mCalcTempMatrix
					.setRectToRect(mSrcRect, mDstRect, ScaleToFit.CENTER);
			outRect_of_outCircle(mSrcRect, x1, y1, x2, y2);
		}
		mZoomMatrix.postConcat(mCalcTempMatrix);
	}

	private void calcAdjuctMatrix() {
		setMinSize();
		setAdjPosition();
		mZoomMatrix.set(mPositionMatrix);
	}

	private void setMinSize() {
		// now rect
		mAdjSizeMatrix.set(mZoomMatrix);
		if (flagMinSize == MinSize.NONE) {
			return;
		}

		float scale = 1f;
		RectF adjSizeRect = new RectF();
		mZoomMatrix.mapRect(adjSizeRect, mOriginalRect);
		outRect(adjSizeRect);
		if (flagMinSize == MinSize.PAGE) {
			scale = Math.min(mViewRect.width() / adjSizeRect.width(),
					mViewRect.height() / adjSizeRect.height());
		} else if (flagMinSize == MinSize.WIDTH) {
			scale = mViewRect.width() / adjSizeRect.width();
		}

		if (scale > 1) {
			mAdjSizeMatrix.postScale(scale, scale, adjSizeRect.centerX(),
					adjSizeRect.centerY());
		}
	}

	private void setAdjPosition() {
		setMinSize();

		mPositionMatrix.reset();
		mPositionMatrix.set(mAdjSizeMatrix);

		if (flagPosition == Position.NONE) {
			return;
		}

		RectF rcSrc = new RectF(mOriginalRect);
		mPositionMatrix.mapRect(rcSrc);
		final RectF rcBase = mViewRect;

		// ìƒ�ìˆ˜
		float cx = (rcBase.width() - rcSrc.width()) / 2f;
		float cy = (rcBase.height() - rcSrc.height()) / 2f;
		float dx = 0;
		float dy = 0;
		if (rcSrc.width() < rcBase.width()) {
			if (flagPosition == Position.INSIDE) {
				if (rcSrc.right > rcBase.right)
					dx = rcBase.right - rcSrc.right;
				else if (rcSrc.left < rcBase.left)
					dx = rcBase.left - rcSrc.left;
				else
					dx = 0;
			} else if (flagPosition == Position.CENTERIN) {
				dx = cx - rcSrc.left;
			}
		} else {
			if (rcSrc.right < rcBase.right)
				dx = rcBase.right - rcSrc.right;
			else if (rcSrc.left > rcBase.left)
				dx = rcBase.left - rcSrc.left;
			else
				dx = 0;
		}

		if (rcSrc.height() < rcBase.height()) {
			if (flagPosition == Position.INSIDE) {
				if (rcSrc.bottom > rcBase.bottom)
					dy = rcBase.bottom - rcSrc.bottom;
				else if (rcSrc.top < rcBase.top)
					dy = rcBase.top - rcSrc.top;
				else
					dy = 0;
			} else if (flagPosition == Position.CENTERIN) {
				dy = cy - rcSrc.top;
			}
		} else {
			if (rcSrc.bottom < rcBase.bottom)
				dy = rcBase.bottom - rcSrc.bottom;
			else if (rcSrc.top > rcBase.top)
				dy = rcBase.top - rcSrc.top;
			else
				dy = 0;
		}

		mPositionMatrix.postTranslate(dx, dy);
	}

	private void outRect_of_outCircle(final RectF rc, float x1, float y1,
			float x2, float y2) {
		float cx = (x1 + x2) / 2f;
		float cy = (y1 + y2) / 2f;

		float x = x1 - x2;
		float y = y1 - y2;
		float r = FloatMath.sqrt(x * x + y * y) / 2f;

		rc.set(cx - r, cy - r, cx + r, cy + r);
	}

	private void outRect(final RectF rc) {
		rc.set(Math.min(rc.left, rc.right), Math.min(rc.top, rc.bottom),
				Math.max(rc.left, rc.right), Math.max(rc.top, rc.bottom));
	}

	// debug block
	public OnDebugListener onDebugListener;

	public static interface OnDebugListener {
		void onDebugData(ZoomState data);
	}

	public void setOnDebugListener(OnDebugListener onDebugListener) {
		this.onDebugListener = onDebugListener;
	}

	public void _dump(final RectF viewRc, final RectF orgRc, final RectF srcRc,
			final RectF dstRc, float[] src, float[] dst) {
		viewRc.set(mViewRect);
		orgRc.set(mOriginalRect);
		srcRc.set(mSrcRect);
		dstRc.set(mDstRect);
		src[0] = mSrc[0];
		src[1] = mSrc[1];
		src[2] = mSrc[2];
		src[3] = mSrc[3];
		dst[0] = mDst[0];
		dst[1] = mDst[1];
		dst[2] = mDst[2];
		dst[3] = mDst[3];
	}

	public void getZoomRect(RectF dst) {
		dst.set(mOriginalRect);
		mZoomMatrix.mapRect(dst);
	}

	public void getMinSize(RectF dst) {
		setMinSize();
		dst.set(mOriginalRect);
		mAdjSizeMatrix.mapRect(dst);
	}

	public void getAdjPosition(RectF dst) {
		setAdjPosition();
		dst.set(mOriginalRect);
		mPositionMatrix.mapRect(dst);
	}

	@Override
	protected void setChanged() {
		super.setChanged();
		if (onDebugListener != null) {
			onDebugListener.onDebugData(this);
		}
	}

	public void setOnModePagerListener(OnModePagerListener listener) {
		this.listener = listener;
	}

	public void exitZoomMode() {
		listener.onModePagerListener();
	}

}
